/*
 *  mih_message.c
 *  MIHF
 *
 *  Created by injae song on 11. 3. 4..
 *  Copyright 2011 KyungHee Univ. All rights reserved.
 *
 */

#include "mih_message.h"

mih_message *new_mih_message(mih_sid sid, mih_opcode opcode, int aid) {
	
	mih_message *message;
	message = alloc_mih_message();
	message->header->sid = sid;
	message->header->opcode = opcode;
	message->header->aid = aid;
	
	return message;
}

mih_message* alloc_mih_message(void) {
	
	mih_message *message;
	message = malloc(sizeof(mih_message));
	message->payload = malloc(sizeof(List));
	list_init(message->payload, &payload_destory);
	message->header = malloc(sizeof(mih_header));
	memset(message->header, 0, sizeof(mih_header));
	
	return message;
}

void payload_destory(void* data)
{
	free(data);
}


generic_tlv *new_tlv(unsigned int type, char *data, int length) 
{
	generic_tlv *tlv;
	tlv = alloc_tlv(length);
	
	memcpy(tlv->value, data, length);
	tlv->type = type; // 4 octets
	
	return tlv;
}

generic_tlv *alloc_tlv(int length) {
	
	generic_tlv *tlv = malloc(sizeof(generic_tlv));
	
	tlv->data_payload_length = length;
	
	unsigned int numbytes_length; // 2 B length ...
	
	if(tlv->data_payload_length <= 128) {
		tlv->tlv_length = tlv->data_payload_length + 4 + 1; // type + 1 octet of length
	} else {
		// FIXME: could be more than 4 ...
		numbytes_length = 3; // unsigned length
		tlv->tlv_length = tlv->data_payload_length + 4 + 1 + numbytes_length; // type + 1 octet of length + variable length
	}
	
	tlv->value = malloc(tlv->data_payload_length);
	bzero(tlv->value, tlv->data_payload_length);
	
	return tlv;
}

char *generic_tlv_serialize(generic_tlv *tlv) {
	
	char *tlv_data;
	
	tlv_data = malloc(tlv->tlv_length);
	
	//To Do : Current Supported Only Payload_Length is just 4byte what ever payload size greater than 128 octet
	memcpy(tlv_data, &tlv->type, 4);
	memcpy(tlv_data + 4, &tlv->data_payload_length, 4);
	memcpy(tlv_data + 4 + 4, tlv->value, tlv->data_payload_length);
	
	return tlv_data;
	
}

char *mih_message_serialize(mih_message* message) {
	
	ListElmt *elem;
	
	char *mih_frame = (char *) malloc(message->header->payloadlength + MIH_HEADER_SIZE);
	bzero(mih_frame, message->header->payloadlength + MIH_HEADER_SIZE);
	
	// write header
	memcpy(mih_frame, message->header, MIH_HEADER_SIZE);
	
	char *w_ptr = mih_frame;
	w_ptr += MIH_HEADER_SIZE;
	
	// write tlvs
	generic_tlv* _tlv = NULL;
	char *serialized_tlv;
	elem =list_head(message->payload);
	
	while (elem !=NULL) {
		
		_tlv =(generic_tlv*) list_data(elem);
		
		serialized_tlv = generic_tlv_serialize(_tlv);
		memcpy(	w_ptr, serialized_tlv, _tlv->tlv_length);
		free(serialized_tlv);
		w_ptr += _tlv->tlv_length;
		
		elem=list_next(elem);
	}
	
	return mih_frame;
}

mih_message *mih_message_parse(char *mih_frame) {
	
	mih_message *message;	
	char *r_ptr;
	generic_tlv* tlv;
	unsigned long tlv_type;
	unsigned int tlv_data_length;
	unsigned int tlv_data_dynamic_length;
	
	message = alloc_mih_message();
	
	// we read the header
	r_ptr = mih_frame;
	
	memcpy(message->header, r_ptr, MIH_HEADER_SIZE);
	
	r_ptr =  mih_frame + MIH_HEADER_SIZE;
	int readbytes = 0;
	
	// till we havent read all payload
	while(readbytes < message->header->payloadlength)
	{
		
		tlv_type = 0;
		tlv_data_dynamic_length = tlv_data_length = 0;
		tlv = NULL;
		
		memcpy(&tlv_type, r_ptr, 4);
		memcpy(&tlv_data_length, r_ptr + 4,	4); //Just Tlv payload length is 4Byte
		
		tlv = alloc_tlv(tlv_data_length);
		tlv->type = tlv_type;
		tlv->data_payload_length = tlv_data_length;
		tlv->tlv_length = tlv->data_payload_length + 4 + 4;
		
		memcpy(tlv->value,	r_ptr + 4 + 4, tlv->data_payload_length);
		
		list_ins_next(message->payload, list_tail(message->payload),(void*) tlv);
		readbytes += tlv->tlv_length;
		r_ptr = mih_frame + MIH_HEADER_SIZE + readbytes;
	}
	
	return message;
}

void mih_add_local_src_tlv(mih_message* msg, mihf *mih) {
	char *src_mihfid = mih->mih_id;
	generic_tlv *src_mihfid_tlv = new_tlv(	MIHF_ID_SRC_TYPE, src_mihfid, strlen(src_mihfid)+1);
	mih_message_add_tlv(msg, src_mihfid_tlv);
}

void mih_add_dst_tlv(mih_message* msg, char* peer_id, mihf *mih)
{
	char *dst_mihfid = peer_id;
	generic_tlv *dst_mihfid_tlv = new_tlv( MIHF_ID_DST_TYPE, dst_mihfid, strlen(dst_mihfid)+1);
	mih_message_add_tlv(msg, dst_mihfid_tlv);
}

void mih_message_add_tlv(mih_message* msg, generic_tlv *tlv) {
	list_ins_next(msg->payload, list_tail(msg->payload), (void*) tlv);
	
	msg->header->payloadlength += tlv->tlv_length; // unsigned ints
	return;
}

char *get_dst_mihfid(mih_message* msg) {
	
	generic_tlv *tlv;
	char *mihfid;
	
	tlv = get_tlv(msg, MIHF_ID_DST_TYPE);
	
	if(tlv) {
		mihfid = malloc(tlv->data_payload_length);
		memcpy(mihfid, tlv->value, tlv->data_payload_length);
		return mihfid;
	} 
	else 
	{
		return NULL;
	}
}

char *get_src_mihfid(mih_message* msg) {
	generic_tlv *tlv;
	char *mihfid;
	tlv = get_tlv(msg, MIHF_ID_SRC_TYPE);
	
	if(tlv) 
	{
		mihfid = malloc(tlv->data_payload_length);
		memcpy(mihfid, tlv->value, tlv->data_payload_length);
		return mihfid;
	} else {
		return NULL;
	}
}

generic_tlv *get_tlv(mih_message *msg, tlv_type type) {
	
	generic_tlv *_tlv = NULL;
	int found = 0;
	ListElmt *elem =list_head(msg->payload);
	
	while (elem !=NULL) {
		
		_tlv = (generic_tlv*) list_data(elem);
		
		if(_tlv->type == type) {
			found = 1;
			break;
		}
		
		elem=list_next(elem);
	}
	
	if(found == 1) {
		return _tlv;
	} else {
		return NULL;
	}
}